#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h> // 全局变量（临界资源），所有库函数都会修改它
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <pthread.h> // POSIX thread
#include <semaphore.h> // for sem_post()/sem_wait()

int balance = 0; // 公共账户

pthread_mutex_t m;
pthread_cond_t cond;

pthread_t tid;

// 此函数将会在对应线程意外终止时自动被调用
void handler(void *arg)
{
    printf("[%lu]调用遗书函数\n", pthread_self());
    pthread_mutex_unlock(&m);
}

void sighandler(int sig)
{
    printf("[%lu]收到%d\n", pthread_self(), sig);
    pthread_exit(NULL);
}

void *routine(void *arg)
{
    signal(SIGINT, sighandler);

    printf("线程[%zu]\n", pthread_self());

    while(1)
    {
        // 准备好遗书
        pthread_cleanup_push(handler, NULL);

        // 遗书函数已准备好，安心加锁
        // 若在持有互斥锁期间意外死亡，系统会为我自动调用遗书函数
        pthread_mutex_lock(&m);

        while(balance <= 0)
        {
            // 进入条件量等待队列，等待其他线程的通知
            // 进入时自动解锁，退出时自动加锁
            pthread_cond_wait(&cond, &m);
        }

        int mm = (100*(rand()%5+1));
        mm = mm > balance ? balance : mm;
        balance -= mm;
        printf("[%zu]取了%d，还剩余%d\n", pthread_self(), mm, balance);

        tid = pthread_self();
        

        // 没发生意外，正常解锁
        pthread_mutex_unlock(&m);
        sleep(3);
        // 解锁之后，弹出没用到的遗书函数避免浪费存储空间
        // 0代表不调用该遗书函数
        pthread_cleanup_pop(0);
    }
}

int main(int argc, char const *argv[])
{
    // 0，初始化互斥锁、条件量
    pthread_mutex_init(&m, NULL);
    pthread_cond_init(&cond, NULL);

    // 1，创建若干线程
    pthread_t t[5];
    for(int i=0; i<5; i++)
        pthread_create(&t[i], NULL, routine, NULL);

    sleep(1);
    
    // 2，往公共账户存钱
    pthread_mutex_lock(&m);
    balance += 1200;
    pthread_mutex_unlock(&m);

    // 3，通知线程去取钱
    pthread_cond_broadcast(&cond); // 通知所有人
    // pthread_cond_signal(&cond);    // 通知某个人

    sleep(1);
    printf("主线程tid:%zu\n",pthread_self());
    printf("tid: %zu\n", tid);
    pthread_kill(tid, SIGINT);

    pause();
    return 0;
}
